package cn.zysheep.gateway.filters;

import cn.zysheep.commons.JWTUtil;
import cn.zysheep.constants.CommonConstants;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

/**
 * @version v1.0
 * @Author: lyw
 * @Date: 2021/8/13
 */
@Order(-1)
@Component
@Slf4j
public class AuthorizeFilter implements GlobalFilter {

    /**
     *  处理当前请求，有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
     *
     * @param exchange 请求上下文，里面可以获取Request、Response等信息
     * @param chain 用来把请求委托给下一个过滤器
     * @return {@code Mono<Void>} 返回标示当前过滤器业务结束
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.debug("请求:  {}", exchange.getRequest().getURI().getPath());
        log.info("---------enter gateway interceptor--------");
        String url = exchange.getRequest().getURI().getPath();
        log.info("url---------------{}", url);
        //登录直接放行
        if (StringUtils.contains(url, CommonConstants.LOGIN_URL)) {
            log.info("     login .............. ");
            return chain.filter(exchange);
        }else if(StringUtils.contains(url, CommonConstants.SWAGGER_URL)){//swagger直接放行
            log.info("     swagger2 .............. ");
            return chain.filter(exchange);
        } else {

            //获取token
            ServerHttpRequest request = exchange.getRequest();
            String token = request.getHeaders().getFirst("token");
            //验证token
            Map<String, Object> tokenMap = JWTUtil.parseToken(token);
            log.info("-----{}", tokenMap);
            if (ObjectUtils.isEmpty(tokenMap.get("ERR_CODE"))) {
                request = exchange.getRequest().mutate().headers(httpHeaders -> {
                    httpHeaders.add("tokenMap", JSON.toJSONString(tokenMap));
                }).build();
                ServerWebExchange build = exchange.mutate().request(request).build();
                return chain.filter(build);
            } else {
                return errorInfo(exchange, tokenMap.get("ERR_MSG").toString(), 500);
            }
        }
    }

    /**
     * 返回response
     *
     * @param exchange
     * @param message  异常信息
     * @param status   data中的status
     * @return
     */
    public static Mono<Void> errorInfo(ServerWebExchange exchange, String message, Integer status) {
        // 自定义返回格式
        Map<String, Object> resultMap = new HashMap<>(8);
        resultMap.put("code", status);
        resultMap.put("msg", StringUtils.isBlank(message) ? "服务异常！" : message);
        resultMap.put("data", null);
        return Mono.defer(() -> {
            byte[] bytes;
            try {
                bytes = new ObjectMapper().writeValueAsBytes(resultMap);
            } catch (JsonProcessingException e) {
                log.error("网关响应异常：", e);
                throw new RuntimeException("信息序列化异常");
            } catch (Exception e) {
                log.error("网关响应异常：", e);
                throw new RuntimeException("写入响应异常");
            }
            ServerHttpResponse response = exchange.getResponse();
            response.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON_UTF8.toString());
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            return response.writeWith(Flux.just(buffer));
        });
    }
}
